
'use strict';

var switchCaptureButton = document.getElementById('switchCapture');
var setLocalSdpButton = document.getElementById('setLocalSdpButton');
var setRemoteSdpButton = document.getElementById('setRemoteSdpButton');
var setRemoteCandButton = document.getElementById('setRemoteCandButton');
var hangupButton = document.getElementById('hangupButton');
var pc1StateDiv = document.querySelector('div#pcState');
var pc1IceStateDiv = document.querySelector('div#pcIceState');

var localSDPSel = 'textarea#localSDP';
var localCandsSel = 'textarea#localCandidates';
var remoteSDPSel = 'textarea#answerSDP';
var remoteCandsSel = 'textarea#answerCandidates';

var localSDPTextarea = document.querySelector(localSDPSel);
var localCandidatesTextarea = document.querySelector(localCandsSel);
var remoteSDPTextarea = document.querySelector(remoteSDPSel);
var remoteCandidatesTextarea = document.querySelector(remoteCandsSel);
var localCandidates = new Array();
var remoteCandidates = new Array();
// var servers = null;
var servers = {
  "iceServers": [{
      "url": "stun:121.41.87.159:3478"
  }]
};

var offerOptions = {
  offerToReceiveAudio: 1,
  offerToReceiveVideo: 1
};

var sdpConstraints = {
  'mandatory': {
    'OfferToReceiveAudio': true,
    'OfferToReceiveVideo': true
  }
};



hangupButton.disabled = false;
switchCaptureButton.innerHTML =  'Open Capture';
switchCaptureButton.onclick = onClickSwitchCapture;

setLocalSdpButton.onclick = onClickSetLocalSdp;
setRemoteSdpButton.onclick = onClickSetRemoteSdp;
setRemoteCandButton.onclick = onClickSetRemoteCand;
hangupButton.onclick = hangup;


var startTime;
var localVideo = document.getElementById('localVideo');
var remoteVideo = document.getElementById('remoteVideo');

localVideo.addEventListener('loadedmetadata', function() {
  console.log('Local video videoWidth: ' + this.videoWidth +
    'px,  videoHeight: ' + this.videoHeight + 'px');
});

remoteVideo.addEventListener('loadedmetadata', function() {
  console.log('Remote video videoWidth: ' + this.videoWidth +
    'px,  videoHeight: ' + this.videoHeight + 'px');
});

remoteVideo.onresize = function() {
  console.log('Remote video size changed to ' +
    remoteVideo.videoWidth + 'x' + remoteVideo.videoHeight);
  // We'll use the first onsize callback as an indication that video has started
  // playing out.
  if (startTime) {
    var elapsedTime = window.performance.now() - startTime;
    console.log('Setup time: ' + elapsedTime.toFixed(3) + 'ms');
    startTime = null;
  }
};

var localStream = null;
var answerStream = null;
var pc1;


function onClickSwitchCapture() {
  if(localStream){
    localStream.getTracks().forEach(function (track) {
                    track.stop();
                });
    // localStream.stop();
    localStream = null;
    switchCaptureButton.innerHTML =  'Open Capture';
    console.log('close local stream');
  }else{
    console.log('Requesting local stream');
    switchCaptureButton.disabled = true;
    navigator.mediaDevices.getUserMedia({
      audio: true,
      video: true
    }).then(function (stream) {
      console.log('got local stream');
      attachMediaStream(localVideo, stream);
      localStream = stream;
      switchCaptureButton.disabled = false;
      switchCaptureButton.innerHTML =  'Close Capture';
    })
    .catch(function(e) {
      alert('getUserMedia() error: ' + e.name);
    });
  }

}


function onClickSetLocalSdp() {
  console.log('click set local sdp button');
  var descraw = JSON.parse(localSDPTextarea.value);
  var desc = new RTCSessionDescription(descraw);
  pc1.setLocalDescription(desc
    , function() {
      console.log('setLocalDescription success');
    }
    , function(error){
      console.log('Failed to set local description: ' + error.toString());
    });
}

function onClickSetRemoteSdp() {
  console.log('click set remote sdp button');
  var descraw = JSON.parse(remoteSDPTextarea.value);
  var desc = new RTCSessionDescription(descraw);
  maybeCreatePeerconnection();
  pc1.setRemoteDescription(desc
    , function() {
      console.log('setRemoteDescription success');
    }
    , function(error){
      console.log('Failed to set remote description: ' + error.toString());
    });
}

function onClickSetRemoteCand() {
  console.log('click set remote candidates button');
  var candidates = JSON.parse(remoteCandidatesTextarea.value);
  candidates.forEach(function(e){  
    pc1.addIceCandidate(new RTCIceCandidate(e)); 
  });
}


function hangup() {
  console.log('Ending call');
  if(pc1){
    pc1StateDiv.textContent += ' => ' + pc1.signalingState || pc1.readyState;
    pc1IceStateDiv.textContent += ' => ' + pc1.iceConnectionState;

    pc1.close();
    pc1 = null;
  }

  if(localStream){
    localStream.getTracks().forEach(function (track) {
                    track.stop();
                });
    // localStream.stop();
    localStream = null;
  }

  hangupButton.disabled = false;
}


function maybeCreatePeerconnection(){
  if(pc1) {
    return;
  }

  pc1 = new RTCPeerConnection(servers);
  console.log('Created local peer connection object pc1');
  
  pc1StateDiv.textContent = pc1.signalingState || pc1.readyState;
  pc1IceStateDiv.textContent = pc1.iceConnectionState;

  pc1.onicecandidate = function(e) {
    onIceCandidate(pc1, e);
  };

  pc1.oniceconnectionstatechange = function(event) {
    console.log(' ICE state: ' + pc1.iceConnectionState);
    // console.log('ICE state change event---: ', event);
    pc1IceStateDiv.textContent += ' => ' + pc1.iceConnectionState;
  };

  pc1.onsignalingstatechange = function(){
    var state;
    state = pc1.signalingState || pc1.readyState;
    console.log('pc1 state change, state: ' + state);
    pc1StateDiv.textContent += ' => ' + state;
  };

  pc1.onaddstream = function (e){
    console.log('received remote stream', e);
    // Call the polyfill wrapper to attach the media stream to this element.
    console.log('attach remote stream');
    // attachMediaStream(remoteVideo, e.stream);
    remoteVideo.srcObject = e.stream;
  };

}

function onIceCandidate(pc, event) {
  if (event.candidate) {
    // localCandidatesTextarea.value = event.candidate;
    localCandidates.push(event.candidate);
    localCandidatesTextarea.value = JSON.stringify( localCandidates );
    console.log('local ICE candidate: \n' + event.candidate.candidate);
  }
}




var util_extend = function () {
    var self = this;
    var options, name, src, copy, copyIsArray, clone,
        target = arguments[0] || {},
        i = 1,
        length = arguments.length,
        deep = false;

    // Handle a deep copy situation
    if (typeof target === "boolean") {
        deep = target;

        // Skip the boolean and the target
        target = arguments[i] || {};
        i++;
    }

    // Handle case when target is a string or something (possible in deep
    // copy)
    if (typeof target !== "object" && !self.isFunction(target)) {
        target = {};
    }

    // Extend self itself if only one argument is passed
    if (i === length) {
        target = this;
        i--;
    }

    for (; i < length; i++) {

        // Only deal with non-null/undefined values
        if (( options = arguments[i] ) != null) {

            // Extend the base object
            for (name in options) {
                src = target[name];
                copy = options[name];

                // Prevent never-ending loop
                if (target === copy) {
                    continue;
                }

                // Recurse if we're merging plain objects or arrays
                if (deep && copy && ( self.isPlainObject(copy) ||
                    ( copyIsArray = self.isArray(copy) ) )) {

                    if (copyIsArray) {
                        copyIsArray = false;
                        clone = src && self.isArray(src) ? src : [];

                    } else {
                        clone = src && self.isPlainObject(src) ? src : {};
                    }

                    // Never move original objects, clone them
                    target[name] = self.extend(deep, clone, copy);

                    // Don't bring in undefined values
                } else if (copy !== undefined) {
                    target[name] = copy;
                }
            }
        }
    }

    // Return the modified object
    return target;
}





var _WebrtcStatistics = {
  bytesPrev: null,
  timestampPrev: null,
  sentBytesPrev: null,
  sentTimestampPrev: null,

  parseRecvStatistics: function (results, callback, callbackSent) {
    var self = this;

   // calculate video bitrate
    var bitrate;
    var remoteWidth;
    var remoteHeight;

    var activeCandidatePair = null;
    var remoteCandidate = null;

    Object.keys(results).forEach(function(result) {
      var report = results[result];
      var now = report.timestamp;

      
      if (report.type === 'inboundrtp' && report.mediaType === 'video') {
        // firefox calculates the bitrate for us
        // https://bugzilla.mozilla.org/show_bug.cgi?id=951496
        bitrate = Math.floor(report.bitrateMean / 1024);
      } else if (report.type === 'ssrc' && report.bytesReceived &&
           report.googFrameHeightReceived) {
        remoteWidth = report.googFrameWidthReceived;
        remoteHeight = report.googFrameHeightReceived;
        // chrome does not so we need to do it ourselves
        var bytes = report.bytesReceived;
        if (self.timestampPrev) {
          bitrate = 8 * (bytes - self.bytesPrev) / (now - self.timestampPrev);
          bitrate = Math.floor(bitrate);
        }
        self.bytesPrev = bytes;
        self.timestampPrev = now;
      }

      if (report.type === 'candidatepair' && report.selected ||
          report.type === 'googCandidatePair' &&
          report.googActiveConnection === 'true') {
        activeCandidatePair = report;
      }

      if(report.type === 'outboundrtp' && report.mediaType === 'video'){
        callbackSent('Video Bitrate', Math.floor(report.bitrateMean / 1024) + ' kbps');
      }else if (report.type === 'ssrc' && report.bytesSent &&
           report.googFrameHeightSent) {
        // chrome does not so we need to do it ourselves
        var bytes = report.bytesSent;
        if (self.sentTimestampPrev) {
          var br = 8 * (bytes - self.sentBytesPrev) / (now - self.sentTimestampPrev);
          br = Math.floor(br);
          callbackSent('Video Bitrate', br+' kbps');
          callbackSent('Video Size', report.googFrameWidthSent + 'x' + report.googFrameHeightSent);
        }
        self.sentBytesPrev = bytes;
        self.sentTimestampPrev = now;
      }


    });
  
    if (activeCandidatePair && activeCandidatePair.remoteCandidateId) {
      remoteCandidate = results[activeCandidatePair.remoteCandidateId];
    }
    if (remoteCandidate && remoteCandidate.ipAddress &&
        remoteCandidate.portNumber) {
      callback('Peer', remoteCandidate.ipAddress + ':' + remoteCandidate.portNumber);
    }

    if(bitrate){
      callback('Video Bitrate', bitrate + ' kbps');
    }

    if(remoteHeight){
      callback('Video Size', remoteWidth + 'x' + remoteHeight);
    }

  }
}

function displayStatistics() {
  if(!pc1){
    console.log('Not start yet');
    return ;
  }
  pc1.getStats(null, function(results) {
    
    recvInfoDiv.innerHTML =  'Time: ' + new Date() + '<br/>';
    sentInfoDiv.innerHTML =  'Time: ' + new Date() + '<br/>';

    statistics.parseRecvStatistics(results, function(name, value){
      recvInfoDiv.innerHTML += name + ': ' + value + '<br/>';
    }
    , function(name, value){
      sentInfoDiv.innerHTML += name + ': ' + value + '<br/>';
    });
  });
  
}

var WebrtcStatisticsSection = function () {
    util_extend(this, _WebrtcStatistics);
};

// Display statistics
var recvInfoDiv = document.querySelector('div#recvInfo');
var sentInfoDiv = document.querySelector('div#sentInfo');
var statistics = new WebrtcStatisticsSection();

setInterval(displayStatistics, 1000);


// =============== caller part ================
var offerButton = document.getElementById('offerButton');
if(offerButton){
  offerButton.onclick = onClickOffer;  
}

function onClickOffer() {
  console.log('click Offer button');
  createMyOffer();
}

function createMyOffer(){
  if(!localStream){
    console.log('can NOT create offer beacause no local stream\n');
    return;
  }

  startTime = window.performance.now();
  var videoTracks = localStream.getVideoTracks();
  var audioTracks = localStream.getAudioTracks();
  if (videoTracks.length > 0) {
    console.log('Using video device: ' + videoTracks[0].label);
  }
  if (audioTracks.length > 0) {
    console.log('Using audio device: ' + audioTracks[0].label);
  }

  maybeCreatePeerconnection();

  console.log('Added local stream to pc1');
  pc1.addStream(localStream);
  
  console.log('pc1 createOffer start');
  //pc1.createOffer(offerOptions).then(
  pc1.createOffer().then(
    function (desc){
      console.log('Offer from pc1\n' + desc.sdp);
      localSDPTextarea.value = JSON.stringify( desc );
    }
    , function (error){
      console.log('Failed to create session description: ' + error.toString());
    }
  );
}


// =============== callee part ================
var answerButton = document.getElementById('answerButton');
if(answerButton){
  answerButton.onclick = function() {
    createMyAnswer();
  };  
}
var pranswerButton = document.getElementById('pranswerButton');
if(pranswerButton){
  pranswerButton.onclick = function() {
    createMyPranswer();
  };  
}

function createMyPranswer(){
  if(localStream){
    console.log('can NOT create pranswer beacause exist local stream\n');
    return;
  }

  pc1.createAnswer(
    function (desc){
      desc.type = "pranswer";
      desc.sdp = desc.sdp.replace(/a=recvonly/g, 'a=inactive');
      console.log('pranswer from pc1\n' + desc.sdp);
      localSDPTextarea.value = JSON.stringify( desc );
    }
    , function (error){
      console.log('Failed to create session description: ' + error.toString());
    }
    , sdpConstraints
  );
}

function createMyAnswer(){
  if(!localStream){
    console.log('can NOT create answer beacause no local stream\n');
    return;
  }

  startTime = window.performance.now();
  var videoTracks = localStream.getVideoTracks();
  var audioTracks = localStream.getAudioTracks();
  if (videoTracks.length > 0) {
    console.log('Using video device: ' + videoTracks[0].label);
  }
  if (audioTracks.length > 0) {
    console.log('Using audio device: ' + audioTracks[0].label);
  }

  console.log('Added local stream to pc1');
  pc1.addStream(localStream);

  pc1.createAnswer(
    function (desc){
      console.log('answer from pc1\n' + desc.sdp);
      localSDPTextarea.value = JSON.stringify( desc );
    }
    , function (error){
      console.log('Failed to create session description: ' + error.toString());
    }
    , sdpConstraints
  );
}


